AttackHelper = {}
AttackHelper.__index = AttackHelper
 
-- Constructor
function AttackHelper.new()
    local instance = {
    }
    
    setmetatable(instance, AttackHelper)
    return instance
end

function AttackHelper:showAttackRange(range, x1, y1, x2, y2)
  if range == 1 then
    self:createOneRange(x1, y1, x2, y2)
  elseif range == 2 then
    self:createTwoRange(x1, y1, x2, y2)
  elseif range == 3 then
    self:createThreeRange(x1, y1, x2, y2)
  end
end

function AttackHelper:createTwoRange(x1, y1, x2, y2)
  local prop1 = self:createProp(x1, y1, x2, y2, 64,    0)
  local prop2 = self:createProp(x1, y1, x2, y2, 0 ,    128)
  local prop3 = self:createProp(x1, y1, x2, y2, 0 ,    64)
  local prop4 = self:createProp(x1, y1, x2, y2, 128 ,  0)
  local prop5 = self:createProp(x1, y1, x2, y2, 0 ,    -64)
  local prop6 = self:createProp(x1, y1, x2, y2, 0 ,    -128)
  local prop7 = self:createProp(x1, y1, x2, y2, -64 ,  0)  
  local prop8 = self:createProp(x1, y1, x2, y2, -128 , 0)
  local prop9 = self:createProp(x1, y1, x2, y2, 64 ,   64)
  local prop10 = self:createProp(x1, y1, x2, y2, -64,  -64)
  local prop11 = self:createProp(x1, y1, x2, y2, -64,  64)
  local prop12 = self:createProp(x1, y1, x2, y2, 64,  -64)
 
  table.insert(attackProps, table.getn(attackProps) + 1, prop1)
  table.insert(attackProps, table.getn(attackProps) + 1, prop2)
  table.insert(attackProps, table.getn(attackProps) + 1, prop3)
  table.insert(attackProps, table.getn(attackProps) + 1, prop4)
  table.insert(attackProps, table.getn(attackProps) + 1, prop5)
  table.insert(attackProps, table.getn(attackProps) + 1, prop6)
  table.insert(attackProps, table.getn(attackProps) + 1, prop7)
  table.insert(attackProps, table.getn(attackProps) + 1, prop8)
  table.insert(attackProps, table.getn(attackProps) + 1, prop9)
  table.insert(attackProps, table.getn(attackProps) + 1, prop10)
  table.insert(attackProps, table.getn(attackProps) + 1, prop11)
  table.insert(attackProps, table.getn(attackProps) + 1, prop12)
end

function AttackHelper:createThreeRange(x1, y1, x2, y2) 
  local prop1 = self:createProp(x1, y1, x2, y2, 64,    0)
  local prop2 = self:createProp(x1, y1, x2, y2, 0 ,    128)
  local prop3 = self:createProp(x1, y1, x2, y2, 0 ,    64)
  local prop4 = self:createProp(x1, y1, x2, y2, 128 ,  0)
  local prop5 = self:createProp(x1, y1, x2, y2, 0 ,    -64)
  local prop6 = self:createProp(x1, y1, x2, y2, 0 ,    -128)
  local prop7 = self:createProp(x1, y1, x2, y2, -64 ,  0)  
  local prop8 = self:createProp(x1, y1, x2, y2, -128 , 0)
  local prop9 = self:createProp(x1, y1, x2, y2, 64 ,   64)
  local prop10 = self:createProp(x1, y1, x2, y2, -64,  -64)
  local prop11 = self:createProp(x1, y1, x2, y2, -64,  64)
  local prop12 = self:createProp(x1, y1, x2, y2, 64,  -64)
  local prop13 = self:createProp(x1, y1, x2, y2, 64 , 128)
  local prop14 = self:createProp(x1, y1, x2, y2, 64 , -128)
  local prop15 = self:createProp(x1, y1, x2, y2, -64 , 128)
  local prop16 = self:createProp(x1, y1, x2, y2, -64 , -128)
  local prop17 = self:createProp(x1, y1, x2, y2, 0 , -192)
  local prop18 = self:createProp(x1, y1, x2, y2, 0 , 192)
  local prop19 = self:createProp(x1, y1, x2, y2, 192 , 0)
  local prop20 = self:createProp(x1, y1, x2, y2, -192 , 0)
  local prop21 = self:createProp(x1, y1, x2, y2, 128 , 64)
  local prop22 = self:createProp(x1, y1, x2, y2, 128 , -64)
  local prop23 = self:createProp(x1, y1, x2, y2, -128 , -64)
  local prop24 = self:createProp(x1, y1, x2, y2, -128 , 64)
  
  table.insert(attackProps, table.getn(attackProps) + 1, prop1)
  table.insert(attackProps, table.getn(attackProps) + 1,  prop2)
  table.insert(attackProps, table.getn(attackProps) + 1, prop3)
  table.insert(attackProps, table.getn(attackProps) + 1, prop4)
  table.insert(attackProps, table.getn(attackProps) + 1, prop5)
  table.insert(attackProps, table.getn(attackProps) + 1, prop6)
  table.insert(attackProps, table.getn(attackProps) + 1, prop7)
  table.insert(attackProps, table.getn(attackProps) + 1, prop8)
  table.insert(attackProps, table.getn(attackProps) + 1, prop9)
  table.insert(attackProps, table.getn(attackProps) + 1, prop10)
  table.insert(attackProps, table.getn(attackProps) + 1, prop11)
  table.insert(attackProps, table.getn(attackProps) + 1, prop12)
  table.insert(attackProps, table.getn(attackProps) + 1, prop13)
  table.insert(attackProps, table.getn(attackProps) + 1, prop14)
  table.insert(attackProps, table.getn(attackProps) + 1, prop15)
  table.insert(attackProps, table.getn(attackProps) + 1, prop16)
  table.insert(attackProps, table.getn(attackProps) + 1, prop17)
  table.insert(attackProps, table.getn(attackProps) + 1, prop18)
  table.insert(attackProps, table.getn(attackProps) + 1, prop19)
  table.insert(attackProps, table.getn(attackProps) + 1, prop20)
  table.insert(attackProps, table.getn(attackProps) + 1, prop21)
  table.insert(attackProps, table.getn(attackProps) + 1, prop22)
  table.insert(attackProps, table.getn(attackProps) + 1, prop23)
  table.insert(attackProps, table.getn(attackProps) + 1, prop24)
end

function AttackHelper:createOneRange(x1, y1, x2, y2) 
  local prop1 = self:createProp(x1, y1, x2, y2, 64, 0)
  local prop2 = self:createProp(x1, y1, x2, y2, 0 , 64)
  local prop3 = self:createProp(x1, y1, x2, y2, 0 , -64)
  local prop4 = self:createProp(x1, y1, x2, y2, -64 , 0)  
 
  table.insert(attackProps, table.getn(attackProps) + 1, prop1)
  table.insert(attackProps, table.getn(attackProps) + 1, prop2)
  table.insert(attackProps, table.getn(attackProps) + 1, prop3)
  table.insert(attackProps, table.getn(attackProps) + 1, prop4)
end

function AttackHelper:removeAttackProps()
  for i=table.getn(attackProps), 1, -1 do
    local prop = attackProps[i]
    MainLayer:removeProp(prop)
    table.remove(attackProps, i)
  end
  attackPossibilitiesOn = false
end

function AttackHelper:createProp(x1, y1, x2, y2, x, y)
  local xN = (x2 + x)
  local yN = -(y2 + y)
  
  local props = screenActionHelper:getObjWithProps(xN, yN)
  local envo = props[1]
  local prop = nil
  
  if envo[1] ~= nil then
    if envo[1]:getUnit() ~= nil and envo[1]:getUnit():getOwnerObject() ~= currentPlayerTurn then

      local sprite = MOAIGfxQuad2D.new()
      sprite:setRect(x1 + x, y2 + y, x2 + x, y1 + y)
      sprite:setTexture("resources/graphics/ingame_menu/HighlightAttack.png")
      
      prop = MOAIProp2D.new()
      prop:setPriority(100)
      prop:setDeck(sprite)
      prop:setBlendMode(MOAIProp.GL_SRC_ALPHA)
      MainLayer:insertProp(prop)
    else
      -- geen unit te vinden
    end
  end
  return prop
end

function AttackHelper:attack(x, y, tile, tileProp, _clickedUnitProp, fromMoving)
  if attackPossibilitiesOn == true then
    local iX, iY = MainLayer:wndToWorld(x, y)
    local props = {gamePartition:propListForPoint(iX, iY, 0, MOAILayer.SORT_PRIORITY_ASCENDING)}
    
    for i, v in pairs(props) do
      for i=1, table.getn(attackProps), 1 do
        local prop = attackProps[i]
        if prop == v then
          local defendTileOn = tile
          local defendTerrain = tile:getWorldObject()
          local defendBuilding = tile:getBuilding()
          local defendUnit = tile:getUnit()
          
          local offensiveTileOn = nil
          local offensiveTerrain = nil
          local offensiveBuilding = nil
          local offensiveUnit = nil
          
          if fromMoving == false then
            offensiveTerrain = lastTile:getWorldObject()
            offensiveBuilding = lastTile:getBuilding()
            offensiveUnit = lastTile:getUnit()
            offensiveTileOn = lastTile
          else
            offensiveTerrain = tileChosenUnitOn:getWorldObject()
            offensiveBuilding = tileChosenUnitOn:getBuilding()
            offensiveUnit = tileChosenUnitOn:getUnit()  
            offensiveTileOn = tileChosenUnitOn
          end
          
          local defenderTileType = defendTerrain:getType()
          local offensiveTileType = offensiveTerrain:getType()
          local attackType = offensiveUnit:getType()
          local defenseType = defendUnit:getType()
          
          local damageDoneToDefendUnit, damageDoneToOffensiveUnit = self:calculateDamage(offensiveUnit, defendUnit, offensiveBuilding, defendBuilding, offensiveTileType, defenderTileType, attackType, defenseType)
          
          self:startSoundAnimation(offensiveUnit, _clickedUnitProp, defendUnit)
          
          local battleTimer = MOAITimer.new()
          battleTimer:setMode(MOAITimer.NORMAL)
          battleTimer:setSpan(0.8)
          battleTimer:setListener(MOAITimer.EVENT_TIMER_END_SPAN, function() 
              
            local ofX1, ofY1, ofX2, ofY2 = offensiveUnit:getRectangleValues()
            local defX1, defY1, defX2, defY2 = defendUnit:getRectangleValues()
            
            local xDif = defX1 - ofX1
            local yDif = defY1 - ofY1
            local isOffensiveDestroyed = nil

            if offensiveUnit:getAttackRange() == 1 or 
              ( offensiveUnit:getAttackRange() > 1 and (xDif >= -64 and xDif <= 64 and yDif == 0) or (yDif >= -64 and yDif <= 64 and xDif == 0)) or 
              (offensiveUnit:getAttackRange() > 1 and defendUnit:getAttackRange() >= offensiveUnit:getAttackRange()) then
              
              isOffensiveDestroyed = offensiveUnit:decreaseHealthBy(damageDoneToOffensiveUnit, chosenUnitProp)
              
              self:showDamageToOffensiveTextboxes(ofX1, ofY1, ofX2, ofY2, damageDoneToOffensiveUnit)
            end
            
            local isDefensiveDestroyed = defendUnit:decreaseHealthBy(damageDoneToDefendUnit, _clickedUnitProp)
            
            self:showDamageToDefensiveTextboxes(defX1, defY1, defX2, defY2, damageDoneToDefendUnit)
            
            if isDefensiveDestroyed == true then
              if defendBuilding ~= nil and defendBuilding:getOwnerObject() ~= defendUnit:getOwnerObject() then
                captureBuilding:removeUnitFromCaptureList(defendUnit)
              end
              defendUnit:getOwnerObject():removeunitFromList(defendUnit)
              defendTileOn:setUnit(nil)
              defendUnit = nil
            end
            
            if isOffensiveDestroyed == true then
              if offensiveBuilding ~= nil and offensiveBuilding:getOwnerObject() ~= offensiveUnit:getOwnerObject() then
                captureBuilding:removeUnitFromCaptureList(offensiveUnit)
              end
              
              offensiveUnit:getOwnerObject():removeunitFromList(offensiveUnit)
              offensiveTileOn:setUnit(nil)
              offensiveUnit = nil
            else
              chosenUnitProp:setColor(1.0,1.0,1.0,0.50)
              table.insert(usedUnits, chosenUnitProp)
              
              offensiveUnit:setHasMoved(true)
              offensiveUnit:setHasAttacked(true)
            end
            
            screenActionHelper:removeAttackRanges()
            
            if unitselectionProp ~= nil then
              MainLayer:removeProp(unitselectionProp)
              unitselectionProp = nil
            end
            attackPossibilitiesOn = false
            return true    
              
          end)
          battleTimer:start()
        end
      end
    end
  end
  return false
end

function AttackHelper:showDamageToOffensiveTextboxes(ofX1, ofY1, ofX2, ofY2, damageDoneToOffensiveUnit)
  local dmgOf = MOAITextBox.new()
  dmgOf:setStyle(styleHealthCaptureBox)
  dmgOf:setRect(ofX1, ofY1 + 25, ofX2, ofY2 + 25)
  dmgOf:setAlignment(MOAITextBox.CENTER_JUSTIFY, MOAITextBox.CENTER_JUSTIFY)
  dmgOf:setYFlip(true)
  dmgOf:setString("- " .. tostring(damageDoneToOffensiveUnit))
  dmgOf:setPriority(60)
  MainLayer:insertProp(dmgOf)
  dmgOf:moveLoc(0,32,0,1)
  
  local timer1 = MOAITimer.new()
  timer1:setMode(MOAITimer.NORMAL)
  timer1:setSpan(1.1)
  timer1:setListener(MOAITimer.EVENT_TIMER_END_SPAN, function() 
      MainLayer:removeProp(dmgOf)
      end)
  timer1:start()
end

function AttackHelper:showDamageToDefensiveTextboxes(defX1, defY1, defX2, defY2, damageDoneToDefendUnit)
  local dmgDef = MOAITextBox.new()
  dmgDef:setStyle(styleHealthCaptureBox)
  dmgDef:setRect(defX1, defY1 + 25, defX2, defY2 + 25)
  dmgDef:setAlignment(MOAITextBox.CENTER_JUSTIFY, MOAITextBox.CENTER_JUSTIFY)
  dmgDef:setYFlip(true)
  dmgDef:setString("- " .. tostring(damageDoneToDefendUnit))
  dmgDef:setPriority(60)
  MainLayer:insertProp(dmgDef)
  dmgDef:moveLoc(0,32,0,1)
  
  local timer = MOAITimer.new()
  timer:setMode(MOAITimer.NORMAL)
  timer:setSpan(1.1)
  timer:setListener(MOAITimer.EVENT_TIMER_END_SPAN, function() 
      MainLayer:removeProp(dmgDef)
      end)
  timer:start()
end

function AttackHelper:calculateDamage(offensiveUnit, defendUnit, offensiveBuilding, defendBuilding, offensiveTileType, defenderTileType, attackType, defenseType)
  local damageDoneToDefendUnit = offensiveUnit:getDamage() * 1.5 -- attacker heeft altijd voordeel met aanvallen
  local damageDoneToOffensiveUnit = defendUnit:getDamage()
  
  if offensiveBuilding == nil then
    damageDoneToDefendUnit = damageDoneToDefendUnit * offensiveUnit:getTerrainModifier(offensiveTileType)
  else
    damageDoneToDefendUnit = damageDoneToDefendUnit * offensiveUnit:getBuildingModifier(offensiveBuilding:getType())
  end
  
  if defendBuilding == nil then
    damageDoneToOffensiveUnit = damageDoneToOffensiveUnit * defendUnit:getTerrainModifier(defenderTileType)
  else
    damageDoneToOffensiveUnit = damageDoneToOffensiveUnit * defendUnit:getBuildingModifier(defendBuilding:getType())
  end
  
  damageDoneToDefendUnit = damageDoneToDefendUnit * offensiveUnit:getUnitModifier(attackType)
  damageDoneToOffensiveUnit = damageDoneToOffensiveUnit * defendUnit:getUnitModifier(defenseType)
  
  damageDoneToDefendUnit = round(damageDoneToDefendUnit)
  damageDoneToOffensiveUnit = round(damageDoneToOffensiveUnit)
  
  if damageDoneToDefendUnit < 1 then
    damageDoneToDefendUnit = 1
  end
  
  if damageDoneToOffensiveUnit < 1 then
    damageDoneToOffensiveUnit = 1
  end
  
  return damageDoneToDefendUnit, damageDoneToOffensiveUnit
end

function AttackHelper:startSoundAnimation(offensiveUnit, _clickedUnitProp, defendUnit)  
  
  if offensiveUnit:getType() == "Heavy_Horse" then
    if math.random(4) <= 2 then
      audio:playKnightAttack1()
    else
      audio:playKnightAttack2()
    end
  elseif offensiveUnit:getType() == "SwordFighter" then
    if math.random(4) <= 2 then
      audio:playSwordFighterAttack1()
    else
      audio:playSwordFighterAttack2()
    end
  elseif offensiveUnit:getType() == "Archer" then
    if math.random(4) <= 2 then
      audio:playArcherAttack1()
    else
      audio:playArcherAttack2()
    end
  end
  
  -- offensive unit animation
  if offensiveUnit:getType() == "Heavy_Horse" then
    if offensiveUnit:getOwnerObject() == pl1 then
      if offensiveUnit:getIsHero() == true then
        self:attackAnimation(knightHeroRedTable, chosenUnitProp, offensiveUnit)
      else
        self:attackAnimation(knightRedTable, chosenUnitProp, offensiveUnit)
      end
    else
      if offensiveUnit:getIsHero() == true then
        self:attackAnimation(knightHeroBlueTable, chosenUnitProp, offensiveUnit)
      else
        self:attackAnimation(knightBlueTable, chosenUnitProp, offensiveUnit)
      end
    end
  elseif offensiveUnit:getType() == "SwordFighter" then
    if offensiveUnit:getOwnerObject() == pl1 then
      if offensiveUnit:getIsHero() == true then
        self:attackAnimation(swordHeroRedTable, chosenUnitProp, offensiveUnit)
      else
        self:attackAnimation(swordRedTable, chosenUnitProp, offensiveUnit)
      end
    else
      if offensiveUnit:getIsHero() == true then
        self:attackAnimation(swordHeroBlueTable, chosenUnitProp, offensiveUnit)
      else
        self:attackAnimation(swordBlueTable, chosenUnitProp, offensiveUnit)
      end
    end
  elseif offensiveUnit:getType() == "Archer" then 
    if offensiveUnit:getOwnerObject() == pl1 then
      if offensiveUnit:getIsHero() == true then
        self:attackAnimation(archerHeroRedTable, chosenUnitProp, offensiveUnit)
      else
        self:attackAnimation(archerRedTable, chosenUnitProp, offensiveUnit)
      end
    else
      if offensiveUnit:getIsHero() == true then
        self:attackAnimation(archerHeroBlueTable, chosenUnitProp, offensiveUnit)
      else
        self:attackAnimation(archerBlueTable, chosenUnitProp, offensiveUnit)
      end
    end
  end
  
  if defendUnit:getAttackRange() >= offensiveUnit:getAttackRange() then
    -- defensive unit animation
    if defendUnit:getType() == "Heavy_Horse" then
      if defendUnit:getOwnerObject() == pl1 then
        if defendUnit:getIsHero() == true then
          self:attackAnimation(knightHeroRedTable, _clickedUnitProp, defendUnit)
        else
          self:attackAnimation(knightRedTable, _clickedUnitProp, defendUnit)
        end
      else
        if defendUnit:getIsHero() == true then
          self:attackAnimation(knightHeroBlueTable, _clickedUnitProp, defendUnit)
        else
          self:attackAnimation(knightBlueTable, _clickedUnitProp, defendUnit)
        end
      end
    elseif defendUnit:getType() == "SwordFighter" then
      if defendUnit:getOwnerObject() == pl1 then
        if defendUnit:getIsHero() == true then
          self:attackAnimation(swordHeroRedTable, _clickedUnitProp, defendUnit)
        else
          self:attackAnimation(swordRedTable, _clickedUnitProp, defendUnit)
        end        
      else
        if defendUnit:getIsHero() == true then
          self:attackAnimation(swordHeroBlueTable, _clickedUnitProp, defendUnit)
        else
          self:attackAnimation(swordBlueTable, _clickedUnitProp, defendUnit)
        end    
      end
    elseif defendUnit:getType() == "Archer" then 
      if defendUnit:getOwnerObject() == pl1 then
        if defendUnit:getIsHero() == true then
          self:attackAnimation(archerHeroRedTable, _clickedUnitProp, defendUnit)
        else
          self:attackAnimation(archerRedTable, _clickedUnitProp, defendUnit)
        end 
      else
        if defendUnit:getIsHero() == true then
          self:attackAnimation(archerHeroBlueTable, _clickedUnitProp, defendUnit)
        else
          self:attackAnimation(archerBlueTable, _clickedUnitProp, defendUnit)
        end 
      end
    end
  end
end

function AttackHelper:attackAnimation(animationTable, prop, unit)
  local checkTime = true
  local deviceTimeStart = MOAISim:getDeviceTime()
  local index = 1
  local indexSprites = 1
  local attackProp = nil
  
  local threadAnimatie = MOAIThread.new()
  threadAnimatie:run(function()
      isAttackAnimationRunning = true
      while checkTime == true do
        local deviceTimeEnd = MOAISim:getDeviceTime()
        
        if (deviceTimeEnd - deviceTimeStart) >= 0.08 then
          prop:setVisible(false)
          
          if attackProp ~= nil then
            MainLayer:removeProp(attackProp)
            attackProp = nil
          end
          
          local sprite = MOAIGfxQuad2D.new()
          
          if unit:getOwnerObject() == pl2 then
            local x1, y1, x2, y2 = unit:getRectangleValues()
            sprite:setRect(x2, y1, x1, y2)
          else
            sprite:setRect(unit:getRectangleValues())
          end
          
          sprite:setTexture(animationTable[indexSprites])
          
          attackProp = MOAIProp2D.new()
          attackProp:setDeck(sprite)
          attackProp:setPriority(30)
          attackProp:setBlendMode(MOAIProp.GL_SRC_ALPHA, MOAIProp.GL_ONE_MINUS_SRC_ALPHA)
          MainLayer:insertProp(attackProp)
          
          deviceTimeStart = deviceTimeEnd
            
          if index == 6 then
            indexSprites = 1
          end
            
          if index >= 12 then
            checkTime = false
            prop:setVisible(true)
            if attackProp ~= nil then
              MainLayer:removeProp(attackProp)
            end
            isAttackAnimationRunning = false
          end
            
          index = index + 1
          indexSprites = indexSprites + 1
        end
        coroutine.yield()
      end
    end )
end

return AttackHelper